package core

import (
	"bufio"
	"bytes"
	"fmt"
	"net"
	"os"
	"path/filepath"
	"time"

	x "gitee.com/shexk/gtl/net"
	"gitee.com/shexk/gtl/protocol"
)

type TransFileInfo struct {
	Command  string //GetDir或Download
	Dir      string
	FileName []string //路径信息或文件名称
	Number   uint32   //子路径数量
	BDir     []byte   //0x00开始，0x01传送数据，0x02传送结束 是否文件夹
	Length   []uint64 //ID或文件大小
}
type DownloadFileInfo struct {
	Command  string //GetDir或Download
	FileName string //路径信息或文件名称
	Flag     byte   //0x00开始，0x01传送数据，0x02传送结束 是否文件夹
	ID       uint64 //ID或文件大小
	Length   uint64
	Index    uint32
	Data     []byte
	Crc      uint16
}

type RequestCmd struct {
	CMDName string //请求的操作命令
	Value   string //请求的值
}

func NewRequestDirCmd(dir string) []byte {
	info := RequestCmd{CMDName: "GetDir", Value: dir}
	buf, _ := protocol.GetBytesFromStruct(info)
	return buf
}

func NewRequestDownloadFileCmd(filepath string) []byte {
	info := RequestCmd{CMDName: "Download", Value: filepath}
	buf, _ := protocol.GetBytesFromStruct(info)
	return buf
}

type FileInfo struct {
	FilePath string
	IsDir    bool
	FileSize uint64
}

func NewFileInfo(filepath string, isdir bool, size uint64) *FileInfo {
	return &FileInfo{FilePath: filepath, IsDir: isdir, FileSize: size}
}

type WriteFileInfo struct {
	FS           *os.File
	Write        *bufio.Writer
	Crc16        *CRC16
	FilePath     string
	FileSize     uint64
	DownLoadSize uint64
	BeginTime    uint64
	EndTime      uint64
}

func NewWriteFileInfo() *WriteFileInfo {
	return &WriteFileInfo{Crc16: NewCRC16()}
}

type Dirs struct {
	Conn           net.Conn
	Files          map[string][]*FileInfo
	RequestDirChan chan *TransFileInfo
	DownloadChan   chan *DownloadFileInfo
	Root           string
	Download       map[uint64]*WriteFileInfo
	TextChan       chan string
}

func NewDirs(txtchan chan string) *Dirs {
	return &Dirs{Files: make(map[string][]*FileInfo),
		RequestDirChan: make(chan *TransFileInfo, 100),
		Download:       make(map[uint64]*WriteFileInfo),
		DownloadChan:   make(chan *DownloadFileInfo, 10000),
		TextChan:       txtchan}
}
func convert(files []*FileInfo) []string {
	dirs := []string{}
	for index := range files {
		dirs = append(dirs, files[index].FilePath)
	}
	return dirs
}
func (d *Dirs) GetFileNameFromFilePath(absPath string) string {
	return filepath.Base(absPath)
}
func (d *Dirs) ProcessData(data []byte) {
	cmd := data[0]
	switch cmd {
	case 0x03:
		{
			data = data[1:]
			info := TransFileInfo{}
			protocol.GetStructFromBytes(data, &info)
			d.RequestDirChan <- &info
		}
	case 0x04:
		{
			data = data[1:]
			dwn := DownloadFileInfo{}
			protocol.GetStructFromBytes(data, &dwn)
			d.DownloadChan <- &dwn
		}
	}
}

func (d *Dirs) StartTask() {
	go func() {
		for data := range d.DownloadChan {
			d.DownloadFile(data)
		}
	}()
}

func (d *Dirs) DownloadFile(data *DownloadFileInfo) {
	switch data.Flag {
	case 0x00: // 头
		id := data.ID
		filepath := data.FileName
		fileSize := data.ID
		file := d.GetFileNameFromFilePath(string(filepath))

		file_Path := GetConfig().DownloadDir + file

		fs, err := os.Create(file_Path)
		if err != nil {
			return
		}
		info := NewWriteFileInfo()
		info.FilePath = file_Path
		info.FS = fs
		info.Write = bufio.NewWriter(fs)
		info.FileSize = fileSize
		info.BeginTime = uint64(time.Now().UnixMilli())
		d.Download[id] = info
	case 0x01:
		id := data.ID
		n := data.Index
		buf := data.Data
		if fi, ok := d.Download[id]; ok {
			fi.Write.Write(buf)
			fi.DownLoadSize += uint64(len(buf))
			fi.Crc16.Calc(buf)
			// 每10次执行一次保存操作
			if n%100 == 0 {
				s := fmt.Sprintf("DownLoad %.2f%%", (float64(fi.DownLoadSize)/float64(fi.FileSize))*100)
				d.TextChan <- s
				fi.Write.Flush()
			}
		}
	case 0x02:
		id := data.ID
		crc16 := data.Crc
		if fi, ok := d.Download[id]; ok {
			fi.EndTime = uint64(time.Now().UnixMilli())
			fi.Write.Flush()
			fi.FS.Close()
			if crc16 != fi.Crc16.Value {
				d.TextChan <- "File Download Fail,CRC16 Error"
			} else {
				d.TextChan <- "File Download Success,CountTime=" + fmt.Sprintf("%d ms", fi.EndTime-fi.BeginTime)
			}

			delete(d.Download, id)
		}
	}
}

func (d *Dirs) RequestDownload(filepath string) {
	if !d.IsDir(filepath) {
		if d.Conn != nil {
			buf := NewRequestDownloadFileCmd(filepath)
			bufs := bytes.NewBuffer(nil)
			bufs.WriteByte(0x02)
			bufs.Write(buf)
			x.WritePackage(d.Conn, bufs.Bytes())
		}
	}
}
func (d *Dirs) RequestDir(dir string) []string {
	if len(dir) == 0 && len(d.Root) > 0 {
		if files, res := d.Files[d.Root]; res {
			return convert(files)
		}
	}
	if len(dir) > 0 {
		if files, res := d.Files[dir]; res {
			return convert(files)
		}
	}

	if d.Conn != nil {
		buf := NewRequestDirCmd(dir)

		bufs := bytes.NewBuffer(nil)
		bufs.WriteByte(0x01)
		bufs.Write(buf)

		x.WritePackage(d.Conn, bufs.Bytes())

		dirs := <-d.RequestDirChan

		result := convert(d.decodeDirInfo(dirs))
		return result
	}

	return nil
}
func (d *Dirs) IsDir(dir string) bool {
	for _, files := range d.Files {
		for index := range files {
			if files[index].FilePath == dir {
				return files[index].IsDir
			}
		}
	}
	return false
}
func (d *Dirs) GetFileSize(filepath string) uint64 {
	for _, files := range d.Files {
		for index := range files {
			if files[index].FilePath == filepath {
				return files[index].FileSize
			}
		}
	}
	return 0
}

func (d *Dirs) decodeDirInfo(data *TransFileInfo) []*FileInfo {
	name := data.Dir

	if d.Root == "" {
		d.Root = string(name)
	}

	result := []*FileInfo{}

	count := data.Number

	for i := 0; i < int(count); i++ {
		bDir := data.BDir
		file := data.FileName[i]
		size := data.Length[i]
		if bDir[i] == 0x00 {
			result = append(result, NewFileInfo(string(file), false, size))
		} else {
			result = append(result, NewFileInfo(string(file), true, size))
		}
	}
	d.Files[string(name)] = result

	return result
}
